//
//  EZSocketAddressUtils.swift
//
//  Created by ellzu gu on 2021/6/29.
//

import Foundation

extension EZSocket {
    public class AddressUtils {
        /// 根据网卡/ip以及协议选择本机地址
        /// - Parameters:
        ///   - local: 网卡名字 或者 网卡IP地址
        ///   - port: 端口
        ///   - family: IP协议 AF_INET or AF_INET6
        /// - Returns: 本机地址
        public static func localAddress(local : String?, port: UInt16 = 0, family : Int32 = 0) -> sockaddr_storage? {
            let addresss: [(sockaddr_storage, String?)] = localAddress(local: local, port: port, familys: family == 0 ? [] : [family])
            return addresss.first?.0
        }
        
        public static func localAddress(local : String?, port: UInt16 = 0, family : Int32 = 0) -> (sockaddr_storage?, String?) {
            let addresss: [(sockaddr_storage, String?)] = localAddress(local: local, port: port, familys: family == 0 ? [] : [family])
            return (addresss.first?.0, addresss.first?.1)
        }
        
        /// 根据网卡/ip以及协议选择本机地址
        /// - Parameters:
        ///   - local: 网卡名字 或者 网卡IP地址
        ///   - port: 端口
        ///   - family: IP协议 AF_INET or AF_INET6
        /// - Returns: (本机地址, 网卡名字)
        public static func localAddress(local : String?, port: UInt16 = 0, familys: [Int32] = []) -> [(sockaddr_storage, String?)] {

            var addresss: [(sockaddr_storage, String?)] = []
            
            var familys = familys
            familys = familys.count == 0 ? self.supportedFamilys : familys
            
            if local?.count ?? 0 == 0 {
                familys.forEach {
                    if let addr = anyAddress(port: port, family: Int($0)) {
                        addresss.append((addr, nil))
                    }
                }
            } else if local!.compare("localhost") == .orderedSame || local!.compare("loopback") == .orderedSame {
                familys.forEach{
                    if let addr = loopbackAddress(port: port, family: Int($0)) {
                        addresss.append((addr, nil))
                    }
                }
            }
            
            if addresss.count > 0 {
                return addresss
            }
            
            var addressPointer : UnsafeMutablePointer<ifaddrs>? = nil;
            guard getifaddrs(&addressPointer) == 0 else {
                return []
            }
            
            var curPointer : UnsafeMutablePointer<ifaddrs>? = addressPointer;
            while curPointer != nil {
                
                let curFamily : sa_family_t = curPointer!.pointee.ifa_addr.pointee.sa_family;
                if familys.count > 0 && !familys.contains(Int32(curFamily)) {
                    curPointer = curPointer?.pointee.ifa_next;
                    continue
                }
                
                var sockaddrStorage : sockaddr_storage? = nil
                var localIfaName: String? = nil
                if let local: String = local {
                    let ifaName : String = String.init(cString: curPointer!.pointee.ifa_name)
                    if ifaName.compare(local) == .orderedSame {
                        let len = Int(curPointer!.pointee.ifa_addr.pointee.sa_len)
                        sockaddrStorage = sockaddr_storage()
                        memcpy(&sockaddrStorage,  curPointer!.pointee.ifa_addr, len)
                        localIfaName = ifaName
                    } else if let ipString: String = curPointer?.pointee.ifa_addr.pointee.ex.ipString(),
                              ipString.compare(local) == .orderedSame {
                        //指定的网卡IP
                        let len = Int(curPointer!.pointee.ifa_addr.pointee.sa_len)
                        sockaddrStorage = sockaddr_storage()
                        memcpy(&sockaddrStorage!, curPointer!.pointee.ifa_addr, len)
                        localIfaName = ifaName
                    }
                }
                
                if var sockaddrStorage: sockaddr_storage = sockaddrStorage {
                    if sockaddrStorage.ss_family == AF_INET6 {
                        let v6Pointer = withUnsafeMutableBytes(of: &sockaddrStorage) { $0.baseAddress! }.assumingMemoryBound(to: sockaddr_in6.self)
                        v6Pointer.pointee.sin6_port = CFSwapInt16(port)
                    } else if sockaddrStorage.ss_family == AF_INET {
                        let v4Pointer = withUnsafeMutableBytes(of: &sockaddrStorage) { $0.baseAddress! }.assumingMemoryBound(to: sockaddr_in.self)
                        v4Pointer.pointee.sin_port = CFSwapInt16(port)
                    }
                    addresss.append((sockaddrStorage, localIfaName))
                }
                curPointer = curPointer?.pointee.ifa_next;
            }
            
            if addressPointer != nil {
                freeifaddrs(addressPointer);
            }
            
            return addresss
        }
        
        /// 获取loopback地址
        /// - Parameters:
        ///   - port: 端口
        ///   - family: 协议 AF_NET or AF_INET6
        /// - Returns: 本机loopback地址
        public static func loopbackAddress(port: UInt16, family: Int) -> sockaddr_storage? {
            let address = anyAddress(port: port, family: family)
            guard var address: sockaddr_storage = address else {
                return nil
            }
            if address.ss_family == AF_INET6 {
                let v6Pointer = withUnsafeMutableBytes(of: &address) { $0.baseAddress! }.assumingMemoryBound(to: sockaddr_in6.self)
                v6Pointer.pointee.sin6_addr = in6addr_loopback
            } else if address.ss_family == AF_INET {
                let v4Pointer = withUnsafeMutableBytes(of: &address) { $0.baseAddress! }.assumingMemoryBound(to: sockaddr_in.self)
                v4Pointer.pointee.sin_addr.s_addr = CFSwapInt32(0x7f000001)
            }
            return address
        }
        
        /// 任意地址
        /// - Parameters:
        ///   - port: 端口
        ///   - family: 协议 AF_NET or AF_INET6
        /// - Returns: 任意地址 = 0.0.0.0
        public static func anyAddress(port: UInt16, family: Int) -> sockaddr_storage? {
            var sockaddr: sockaddr_storage? = nil
            if family == AF_INET6 {
                sockaddr = sockaddr_storage()
                let rawPointer = withUnsafeMutableBytes(of: &sockaddr) { $0.baseAddress! }
                let v6Pointer: UnsafeMutablePointer<sockaddr_in6> = rawPointer.assumingMemoryBound(to: sockaddr_in6.self)
                v6Pointer.pointee.sin6_len = __uint8_t(MemoryLayout<sockaddr_in6>.size)
                v6Pointer.pointee.sin6_family = sa_family_t(AF_INET6)
                v6Pointer.pointee.sin6_port = CFSwapInt16(port)
                v6Pointer.pointee.sin6_addr = in6addr_any
            } else if family == AF_INET {
                sockaddr = sockaddr_storage()
                let rawPointer = withUnsafeMutableBytes(of: &sockaddr) { $0.baseAddress! }
                let v4Pointer: UnsafeMutablePointer<sockaddr_in> = rawPointer.assumingMemoryBound(to: sockaddr_in.self)
                v4Pointer.pointee.sin_len = __uint8_t(MemoryLayout<sockaddr_in>.size)
                v4Pointer.pointee.sin_family = sa_family_t(AF_INET)
                v4Pointer.pointee.sin_port = CFSwapInt16(port)
                v4Pointer.pointee.sin_addr.s_addr = CFSwapInt32(0x00000000)
            }
            return sockaddr
        }
        
        /// 域名转换成目标地址
        /// - Parameters:
        ///   - host: 主机域名或IP
        ///   - hostPort: 端口
        /// - Returns: 目标地址
        public static func addressWithHost(host : String, hostPort : UInt16, family: Int32 = 0) -> sockaddr_storage? {
            let hostAddress: [sockaddr_storage] = addresssWithHost(host: host, hostPort: hostPort, familys: family == 0 ? [] : [family])
            let hostAddres: sockaddr_storage? = hostAddress.first
            return hostAddres
        }
        
        /// 域名转换成目标地址
        /// - Parameters:
        ///   - host: 主机域名或IP
        ///   - hostPort: 端口
        /// - Returns: 一组目标地址
        public static func addresssWithHost(host : String, hostPort : UInt16, familys: [Int32] = []) -> [sockaddr_storage] {
            
            var familys = familys
            familys = familys.count == 0 ? self.supportedFamilys : familys
            
            var template : addrinfo = addrinfo.init();
            template.ai_family = AF_UNSPEC;
            template.ai_socktype = SOCK_DGRAM;
            template.ai_protocol = IPPROTO_IP;
//            template.ai_flags = AI_DEFAULT;
            var addressPointer : UnsafeMutablePointer<addrinfo>? = nil;
            let code : Int32 = getaddrinfo(host.cString(using: .utf8), "", &template, &addressPointer);//这里DNS查询可能需要连接网络
            
            var sockaddrStorages: [sockaddr_storage] = []
            if code == 0 {
                var curPointer : UnsafeMutablePointer<addrinfo>? = addressPointer;
                while curPointer != nil {
                    
                    if familys.count > 0 && !familys.contains(curPointer!.pointee.ai_family) {
                        curPointer = curPointer?.pointee.ai_next;
                        continue
                    }
                    
                    var sockaddrStorage : sockaddr_storage? = nil
                    if (curPointer!.pointee.ai_family == AF_INET) {
                        //发现一个IPV4地址，可能有很多地址
                        sockaddrStorage = sockaddr_storage()
                        let rawPointer = withUnsafeMutableBytes(of: &sockaddrStorage) { $0.baseAddress! }
                        let v4Pointer: UnsafeMutablePointer<sockaddr_in> = rawPointer.assumingMemoryBound(to: sockaddr_in.self)
                        memcpy(v4Pointer, curPointer!.pointee.ai_addr, Int(curPointer!.pointee.ai_addrlen));
                        v4Pointer.pointee.sin_port = CFSwapInt16(hostPort)
                        
                    } else if (curPointer?.pointee.ai_family == AF_INET6) {
                        //发现IPV6地址
                        sockaddrStorage = sockaddr_storage()
                        let rawPointer = withUnsafeMutableBytes(of: &sockaddrStorage) { $0.baseAddress! }
                        let v6Pointer: UnsafeMutablePointer<sockaddr_in6> = rawPointer.assumingMemoryBound(to: sockaddr_in6.self)
                        memcpy(v6Pointer, curPointer!.pointee.ai_addr, Int(curPointer!.pointee.ai_addrlen));
                        v6Pointer.pointee.sin6_port = CFSwapInt16(hostPort)
                    }
                    if let sockaddrStorage: sockaddr_storage = sockaddrStorage {
                        sockaddrStorages.append(sockaddrStorage)
                    }
                    curPointer = curPointer?.pointee.ai_next;
                }
                freeaddrinfo(addressPointer!);
            } else {
//                let errorMsg = String.init(cString: gai_strerror(code));
//                print("getaddrinfo return error :" + errorMsg);
                sockaddrStorages = addresssWithHost_CF(host: host, hostPort: hostPort)
            }
            sockaddrStorages = sockaddrStorages.sorted(by: {
                let index0 = familys.firstIndex(of: Int32($0.ss_family)) ?? familys.endIndex
                let index1 = familys.firstIndex(of: Int32($1.ss_family)) ?? familys.endIndex
                return index0 < index1
            })
            return sockaddrStorages
        }
        public static func addresssWithHost_CF(host : String, hostPort : UInt16, familys: [Int32] = []) -> [sockaddr_storage] {
            
            var sockaddrStorages: [sockaddr_storage] = []
            
            let hostName = host as CFString
            var resolvedRes: DarwinBoolean = false
            let cfHost = CFHostCreateWithName(kCFAllocatorDefault, hostName).takeRetainedValue()
            var streamError = CFStreamError()
            var startRes: Bool = false
            
            let timeout: TimeInterval = 3.0
            let sema = DispatchSemaphore(value: 0)
            DispatchQueue.global().async {
                startRes = CFHostStartInfoResolution(cfHost, .addresses, &streamError)
                sema.signal()
            }
            let semaRes = sema.wait(timeout: .now() + timeout)
            
            repeat {
                
                if semaRes == .timedOut {
                    break
                }
                
                if startRes == false {
                    break
                }
                // handle success
                guard let ipDatas = CFHostGetAddressing(cfHost, &resolvedRes)?.takeUnretainedValue() as Array? else {
                    break
                }
                
                for case let ipData as Data in ipDatas {
                    ipData.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) -> Void in
                        let addrPointer: UnsafePointer<sockaddr> = rawBuffer.baseAddress!.bindMemory(to: sockaddr.self, capacity: rawBuffer.count);
                        var sockaddrStorage : sockaddr_storage? = nil
                        if addrPointer.pointee.sa_family == AF_INET {
                            //发现一个IPV4地址，可能有很多地址
                            sockaddrStorage = sockaddr_storage()
                            let rawPointer = withUnsafeMutableBytes(of: &sockaddrStorage) { $0.baseAddress! }
                            let v4Pointer: UnsafeMutablePointer<sockaddr_in> = rawPointer.assumingMemoryBound(to: sockaddr_in.self)
                            memcpy(v4Pointer, addrPointer, Int(addrPointer.pointee.sa_len));
                            v4Pointer.pointee.sin_port = CFSwapInt16(hostPort)
                            
                        } else if addrPointer.pointee.sa_family == AF_INET6 {
                            sockaddrStorage = sockaddr_storage()
                            let rawPointer = withUnsafeMutableBytes(of: &sockaddrStorage) { $0.baseAddress! }
                            let v6Pointer: UnsafeMutablePointer<sockaddr_in6> = rawPointer.assumingMemoryBound(to: sockaddr_in6.self)
                            memcpy(v6Pointer, addrPointer, Int(addrPointer.pointee.sa_len));
                            v6Pointer.pointee.sin6_port = CFSwapInt16(hostPort)
                        } else {
                            //其他版本的IP暂不支持
                        }
                        
                        if let sockaddrStorage: sockaddr_storage = sockaddrStorage {
                            sockaddrStorages.append(sockaddrStorage)
                        }
                        
                    }
                }
                
            } while false
            
            return sockaddrStorages
        }
        
        /// 支持的IP协议
//        public static let supportedFamilys: [Int32] = [AF_INET6, AF_INET]
        public static let supportedFamilys: [Int32] = [AF_INET]
    }
}

